; 版本：miniOS
; 功能：时间盘轮转，延时调度，无优先级
; 作者：https://gitee.com/Amate123/miniOS
; 时间：2024-11-03
; 修改：适配Cotex-M0内核，R4-R11现场保存和恢复分开进行


	EXPORT		PendSV_Handler
	EXPORT		CPU_INT_Save
	EXPORT		CPU_INT_Restore	
	EXPORT		OS_Trig_PendSV
	EXPORT		Set_PendSV_Lowest
	EXPORT		OS_Run_Highest
	
		
	IMPORT		OSTask_CurTcb
	IMPORT		OSTask_SwTcb
	IMPORT		OSTask_Slp_Chk

; 这里的变量仿照UCOSIII中，但设置PendSV优先级部分有修改
NVIC_INT_CTRL   EQU     0xE000ED04            ; 中断控制状态寄存器
NVIC_SYSPRI14   EQU     0xE000ED20            ; 系统优先级寄存器(参考M0权威指南P134/P158，M3/M4权威指南P161，M0内核不支持非对齐访问)
NVIC_PENDSV_PRI EQU       0xFF0000            ; PendSV优先级设为最低
NVIC_PENDSVSET  EQU     0x10000000            ; 挂起PendSV异常的写入值

	PRESERVE8
    THUMB

    AREA CODE, CODE, READONLY


; 运行最高优先级任务（实际上是就绪链表表头任务）
OS_Run_Highest
				LDR		R0, =OSTask_CurTcb
				LDR		R0, [R0]
				LDR		R0, [R0]
				
				ADDS	R0, #56				   ; 丢弃现场，第一个任务不需要现场
				LDR		R1, [R0]			   ; 从栈中取出返回地址
				ADDS 	R0, #4
				LDR     R2, =OSTask_CurTcb	   ; 更新第一个任务的SP
				LDR     R2, [R2]
				STR     R0, [R2]
				MSR		MSP, R0
				BX		R1


; 设PendSV优先级最低，防止它打断其他中断后直接返回到线程，造成UsageFault
Set_PendSV_Lowest
				LDR     R0, =NVIC_SYSPRI14                                  ; Set the PendSV exception priority
				LDR     R1, =NVIC_PENDSV_PRI
				LDR		R2, [R0]				; 获取可编程系统异常优先级寄存器原本的值
				ORRS	R2, R2, R1
				STR     R2, [R0]				; 设置PendSV可编程优先级为最低
				BX		LR
		
		
; 挂起PendSV，触发调度
OS_Trig_PendSV
				LDR     R0, =NVIC_INT_CTRL                                  ; Trigger the PendSV exception (causes context switch)
				LDR     R1, =NVIC_PENDSVSET
				STR     R1, [R0]                                               ; Enable interrupts at processor level
				BX 		LR


PendSV_Handler	
				CPSID   I
				
				; 保存现场，R4-R11分批保存，适配Cortec-M0内核
				MOV		R0,R8
				MOV		R1,R9
				MOV		R2,R10
				MOV		R3,R11
				STMFD	R13!, {R0-R3}
				STMFD	R13!, {R4-R7}
				
				; 修改当前任务栈顶指针
				MRS		R0, MSP
				LDR		R1, =OSTask_CurTcb
				LDR		R1, [R1]
				STR		R0, [R1]

				; 任务切换，核心是修改OSTask_CurTcb
				PUSH    {R14}
				LDR     R1, =OSTask_SwTcb	 ; 切换任务
				BLX     R1
				POP     {R0}
				MOV		R14,R0
				
				; 恢复现场
				LDR     R1, =OSTask_CurTcb	 ; 获取任务SP地址
				LDR     R1, [R1]
				LDR     R0, [R1]
				
				; R4-R11分多次恢复，适配Cortec-M0内核
				LDMIA	R0!, {R4-R7}	     ; 恢复R4 - R7
				LDMIA	R0!, {R1-R3}	     ; 恢复R8 - R10
				MOV		R8,R1
				MOV		R9,R2
				MOV		R10,R3
				LDMIA	R0!, {R1}
				MOV		R11,R1
				MSR		MSP, R0				 ; 切换成任务栈
				
				; 更新任务SP
				LDR     R1, =OSTask_CurTcb	 ; 获取任务SP地址
				LDR     R1, [R1]
				STR     R0, [R1]
				
				CPSIE   I
				
				BX		LR					 ; 触发异常返回
				

; 保存现场，并关闭中断
CPU_INT_Save
				MRS     R0, PRIMASK                     ; Set prio int mask to mask all (except faults)
				CPSID   I
				BX      LR

; 恢复现场
CPU_INT_Restore                                  ; See Note #2.
				MSR     PRIMASK, R0
				BX      LR



				ALIGN 4
				END	

				